﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace Cyotek.Windows.Forms.Demo
{
	// Cyotek ImageBox
	// Copyright (c) 2010-2015 Cyotek Ltd.
	// http://cyotek.com
	// http://cyotek.com/blog/tag/imagebox

	// Licensed under the MIT License. See license.txt for the full text.

	// If you use this control in your applications, attribution, donations or contributions are welcome.

	internal class ImageBoxEx : ImageBox
	{
		#region Constants

		private readonly DragHandleCollection _dragHandles;

		private static readonly object _eventDragHandleSizeChanged = new object();

		private static readonly object _eventMaximumSelectionSizeChanged = new object();

		private static readonly object _eventMinimumSelectionSizeChanged = new object();

		private static readonly object _eventSelectionMoved = new object();

		private static readonly object _eventSelectionMoving = new object();

		private static readonly object _eventSelectionResized = new object();

		private static readonly object _eventSelectionResizing = new object();

		#endregion

		#region Fields

		private int _dragHandleSize;

		private Point _dragOrigin;

		private Point _dragOriginOffset;

		private bool _isMoving;

		private bool _isResizing;

		private Size _maximumSelectionSize;

		private Size _minimumSelectionSize;

		private RectangleF _previousSelectionRegion;

		private DragHandleAnchor _resizeAnchor;

		#endregion

		#region Constructors

		public ImageBoxEx() {
			_dragHandles = new DragHandleCollection();
			_dragHandleSize = 8;
			_maximumSelectionSize = Size.Empty;
			PositionDragHandles();
		}

		#endregion

		#region Events

		/// <summary>
		/// Occurs when the DragHandleSize property value changes
		/// </summary>
		[Category("Property Changed")]
		public event EventHandler DragHandleSizeChanged {
			add { Events.AddHandler(_eventDragHandleSizeChanged, value); }
			remove { Events.RemoveHandler(_eventDragHandleSizeChanged, value); }
		}

		/// <summary>
		/// Occurs when the MaximumSelectionSize property value changes
		/// </summary>
		[Category("Property Changed")]
		public event EventHandler MaximumSelectionSizeChanged {
			add { Events.AddHandler(_eventMaximumSelectionSizeChanged, value); }
			remove { Events.RemoveHandler(_eventMaximumSelectionSizeChanged, value); }
		}

		/// <summary>
		/// Occurs when the MinimumSelectionSize property value changes
		/// </summary>
		[Category("Property Changed")]
		public event EventHandler MinimumSelectionSizeChanged {
			add { Events.AddHandler(_eventMinimumSelectionSizeChanged, value); }
			remove { Events.RemoveHandler(_eventMinimumSelectionSizeChanged, value); }
		}

		[Category("Action")]
		public event EventHandler SelectionMoved {
			add { Events.AddHandler(_eventSelectionMoved, value); }
			remove { Events.RemoveHandler(_eventSelectionMoved, value); }
		}

		[Category("Action")]
		public event CancelEventHandler SelectionMoving {
			add { Events.AddHandler(_eventSelectionMoving, value); }
			remove { Events.RemoveHandler(_eventSelectionMoving, value); }
		}

		[Category("Action")]
		public event EventHandler SelectionResized {
			add { Events.AddHandler(_eventSelectionResized, value); }
			remove { Events.RemoveHandler(_eventSelectionResized, value); }
		}

		[Category("Action")]
		public event CancelEventHandler SelectionResizing {
			add { Events.AddHandler(_eventSelectionResizing, value); }
			remove { Events.RemoveHandler(_eventSelectionResizing, value); }
		}

		#endregion

		#region Properties

		[Browsable(false)]
		public DragHandleCollection DragHandles => _dragHandles;

		[Category("Appearance")]
		[DefaultValue(8)]
		public virtual int DragHandleSize {
			get => _dragHandleSize;
			set {
				if (_dragHandleSize != value) {
					_dragHandleSize = value;

					OnDragHandleSizeChanged(EventArgs.Empty);
				}
			}
		}

		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public bool IsMoving {
			get => _isMoving;
			protected set => _isMoving = value;
		}

		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public bool IsResizing {
			get => _isResizing;
			protected set => _isResizing = value;
		}

		[Category("Behavior")]
		[DefaultValue(typeof(Size), "0, 0")]
		public virtual Size MaximumSelectionSize {
			get => _maximumSelectionSize;
			set {
				if (MaximumSelectionSize != value) {
					_maximumSelectionSize = value;

					OnMaximumSelectionSizeChanged(EventArgs.Empty);
				}
			}
		}

		[Category("Behavior")]
		[DefaultValue(typeof(Size), "0, 0")]
		public virtual Size MinimumSelectionSize {
			get => _minimumSelectionSize;
			set {
				if (MinimumSelectionSize != value) {
					_minimumSelectionSize = value;

					OnMinimumSelectionSizeChanged(EventArgs.Empty);
				}
			}
		}

		[Browsable(false)]
		public RectangleF PreviousSelectionRegion {
			get => _previousSelectionRegion;
			protected set => _previousSelectionRegion = value;
		}

		protected Point DragOrigin {
			get => _dragOrigin;
			set => _dragOrigin = value;
		}

		protected Point DragOriginOffset {
			get => _dragOriginOffset;
			set => _dragOriginOffset = value;
		}

		protected DragHandleAnchor ResizeAnchor {
			get => _resizeAnchor;
			set => _resizeAnchor = value;
		}

		#endregion

		#region Methods

		public void CancelResize() {
			SelectionRegion = _previousSelectionRegion;
			CompleteResize();
		}

		public void StartMove() {
			CancelEventArgs e;

			if (_isMoving || _isResizing) {
				throw new InvalidOperationException("A move or resize action is currently being performed.");
			}

			e = new CancelEventArgs();

			OnSelectionMoving(e);

			if (!e.Cancel) {
				_previousSelectionRegion = SelectionRegion;
				_isMoving = true;
			}
		}

		protected virtual void DrawDragHandle(Graphics graphics, DragHandle handle) {
			int left;
			int top;
			int width;
			int height;
			Pen outerPen;
			Brush innerBrush;

			left = handle.Bounds.Left;
			top = handle.Bounds.Top;
			width = handle.Bounds.Width;
			height = handle.Bounds.Height;

			if (handle.Enabled) {
				outerPen = SystemPens.WindowFrame;
				innerBrush = SystemBrushes.Window;
			}
			else {
				outerPen = SystemPens.ControlDark;
				innerBrush = SystemBrushes.Control;
			}

			graphics.FillRectangle(innerBrush, left + 1, top + 1, width - 2, height - 2);
			graphics.DrawLine(outerPen, left + 1, top, left + width - 2, top);
			graphics.DrawLine(outerPen, left, top + 1, left, top + height - 2);
			graphics.DrawLine(outerPen, left + 1, top + height - 1, left + width - 2, top + height - 1);
			graphics.DrawLine(outerPen, left + width - 1, top + 1, left + width - 1, top + height - 2);
		}

		/// <summary>
		/// Raises the <see cref="DragHandleSizeChanged" /> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
		protected virtual void OnDragHandleSizeChanged(EventArgs e) {
			EventHandler handler;

			PositionDragHandles();
			Invalidate();

			handler = (EventHandler)Events[_eventDragHandleSizeChanged];

			handler?.Invoke(this, e);
		}

		/// <summary>
		/// Raises the <see cref="MaximumSelectionSizeChanged" /> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
		protected virtual void OnMaximumSelectionSizeChanged(EventArgs e) {
			EventHandler handler;

			handler = (EventHandler)Events[_eventMaximumSelectionSizeChanged];

			handler?.Invoke(this, e);
		}

		/// <summary>
		/// Raises the <see cref="MinimumSelectionSizeChanged" /> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
		protected virtual void OnMinimumSelectionSizeChanged(EventArgs e) {
			EventHandler handler;

			handler = (EventHandler)Events[_eventMinimumSelectionSizeChanged];

			handler?.Invoke(this, e);
		}

		/// <summary>
		///   Raises the <see cref="System.Windows.Forms.Control.MouseDown" /> event.
		/// </summary>
		/// <param name="e">
		///   A <see cref="T:System.Windows.Forms.MouseEventArgs" /> that contains the event data.
		/// </param>
		protected override void OnMouseDown(MouseEventArgs e) {
			Point imagePoint;
			RectangleF selectionRegion;

			imagePoint = PointToImage(e.Location);
			selectionRegion = SelectionRegion;

			if (e.Button == MouseButtons.Left && (selectionRegion.Contains(imagePoint) || HitTest(e.Location) != DragHandleAnchor.None)) {
				_dragOrigin = e.Location;
				_dragOriginOffset = new Point(imagePoint.X - (int)selectionRegion.X, imagePoint.Y - (int)selectionRegion.Y);
			}
			else {
				_dragOriginOffset = Point.Empty;
				_dragOrigin = Point.Empty;
			}

			base.OnMouseDown(e);
		}

		/// <summary>
		///   Raises the <see cref="System.Windows.Forms.Control.MouseMove" /> event.
		/// </summary>
		/// <param name="e">
		///   A <see cref="T:System.Windows.Forms.MouseEventArgs" /> that contains the event data.
		/// </param>
		protected override void OnMouseMove(MouseEventArgs e) {
			// start either a move or a resize operation
			if (!IsSelecting && !_isMoving && !_isResizing && e.Button == MouseButtons.Left && !_dragOrigin.IsEmpty && IsOutsideDragZone(e.Location)) {
				DragHandleAnchor anchor;

				anchor = HitTest(_dragOrigin);

				if (anchor == DragHandleAnchor.None) {
					// move
					StartMove();
				}
				else if (_dragHandles[anchor].
						   Enabled && _dragHandles[anchor].
						   Visible) {
					// resize
					StartResize(anchor);
				}
			}

			// set the cursor
			SetCursor(e.Location);

			// perform operations
			ProcessSelectionMove(e.Location);
			ProcessSelectionResize(e.Location);

			base.OnMouseMove(e);
		}

		/// <summary>
		///   Raises the <see cref="System.Windows.Forms.Control.MouseUp" /> event.
		/// </summary>
		/// <param name="e">
		///   A <see cref="T:System.Windows.Forms.MouseEventArgs" /> that contains the event data.
		/// </param>
		protected override void OnMouseUp(MouseEventArgs e) {
			if (_isMoving) {
				CompleteMove();
			}
			else if (_isResizing) {
				CompleteResize();
			}

			base.OnMouseUp(e);
		}

		/// <summary>
		///   Raises the <see cref="System.Windows.Forms.Control.Paint" /> event.
		/// </summary>
		/// <param name="e">
		///   A <see cref="T:System.Windows.Forms.PaintEventArgs" /> that contains the event data.
		/// </param>
		protected override void OnPaint(PaintEventArgs e) {
			base.OnPaint(e);

			if (AllowPainting && !SelectionRegion.IsEmpty) {
				foreach (DragHandle handle in _dragHandles) {
					if (handle.Visible) {
						DrawDragHandle(e.Graphics, handle);
					}
				}
			}
		}

		/// <summary>
		///   Raises the <see cref="ImageBox.PanStart" /> event.
		/// </summary>
		/// <param name="e">
		///   The <see cref="System.ComponentModel.CancelEventArgs" /> instance containing the event data.
		/// </param>
		protected override void OnPanStart(CancelEventArgs e) {
			if (_isMoving || _isResizing || !_dragOrigin.IsEmpty) {
				e.Cancel = true;
			}

			base.OnPanStart(e);
		}

		/// <summary>
		///   Raises the <see cref="System.Windows.Forms.Control.Resize" /> event.
		/// </summary>
		/// <param name="e">
		///   An <see cref="T:System.EventArgs" /> that contains the event data.
		/// </param>
		protected override void OnResize(EventArgs e) {
			base.OnResize(e);

			PositionDragHandles();
		}

		/// <summary>
		///   Raises the <see cref="System.Windows.Forms.ScrollableControl.Scroll" /> event.
		/// </summary>
		/// <param name="se">
		///   A <see cref="T:System.Windows.Forms.ScrollEventArgs" /> that contains the event data.
		/// </param>
		protected override void OnScroll(ScrollEventArgs se) {
			base.OnScroll(se);

			PositionDragHandles();
		}

		/// <summary>
		///   Raises the <see cref="ImageBox.Selecting" /> event.
		/// </summary>
		/// <param name="e">
		///   The <see cref="System.EventArgs" /> instance containing the event data.
		/// </param>
		protected override void OnSelecting(ImageBoxCancelEventArgs e) {
			e.Cancel = _isMoving || _isResizing || SelectionRegion.Contains(PointToImage(e.Location)) || HitTest(e.Location) != DragHandleAnchor.None;

			base.OnSelecting(e);
		}

		/// <summary>
		/// Raises the <see cref="SelectionMoved" /> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
		protected virtual void OnSelectionMoved(EventArgs e) {
			EventHandler handler;

			handler = (EventHandler)Events[_eventSelectionMoved];

			handler?.Invoke(this, e);
		}

		/// <summary>
		/// Raises the <see cref="SelectionMoving" /> event.
		/// </summary>
		/// <param name="e">The <see cref="CancelEventArgs" /> instance containing the event data.</param>
		protected virtual void OnSelectionMoving(CancelEventArgs e) {
			CancelEventHandler handler;

			handler = (CancelEventHandler)Events[_eventSelectionMoving];

			handler?.Invoke(this, e);
		}

		/// <summary>
		///   Raises the <see cref="ImageBox.SelectionRegionChanged" /> event.
		/// </summary>
		/// <param name="e">
		///   The <see cref="System.EventArgs" /> instance containing the event data.
		/// </param>
		protected override void OnSelectionRegionChanged(EventArgs e) {
			base.OnSelectionRegionChanged(e);

			PositionDragHandles();
		}

		/// <summary>
		/// Raises the <see cref="SelectionResized" /> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
		protected virtual void OnSelectionResized(EventArgs e) {
			EventHandler handler;

			handler = (EventHandler)Events[_eventSelectionResized];

			handler?.Invoke(this, e);
		}

		/// <summary>
		/// Raises the <see cref="SelectionResizing" /> event.
		/// </summary>
		/// <param name="e">The <see cref="CancelEventArgs" /> instance containing the event data.</param>
		protected virtual void OnSelectionResizing(CancelEventArgs e) {
			CancelEventHandler handler;

			handler = (CancelEventHandler)Events[_eventSelectionResizing];

			handler?.Invoke(this, e);
		}

		/// <summary>
		///   Raises the <see cref="ImageBox.ZoomChanged" /> event.
		/// </summary>
		/// <param name="e">
		///   The <see cref="System.EventArgs" /> instance containing the event data.
		/// </param>
		protected override void OnZoomChanged(EventArgs e) {
			base.OnZoomChanged(e);

			PositionDragHandles();
		}

		/// <summary>
		/// Processes a dialog key.
		/// </summary>
		/// <returns>
		/// true if the key was processed by the control; otherwise, false.
		/// </returns>
		/// <param name="keyData">One of the <see cref="T:System.Windows.Forms.Keys"/> values that represents the key to process. </param>
		protected override bool ProcessDialogKey(Keys keyData) {
			bool result;

			if (keyData == Keys.Escape && (_isResizing || _isMoving)) {
				if (_isResizing) {
					CancelResize();
				}
				else {
					CancelMove();
				}

				result = true;
			}
			else {
				result = base.ProcessDialogKey(keyData);
			}

			return result;
		}

		protected virtual void SetCursor(Point point) {
			// http://forums.cyotek.com/imagebox/cursor-issue-in-imageboxex/msg92/#msg92

			if (!IsPanning) {
				Cursor cursor;

				if (IsSelecting) {
					cursor = Cursors.Default;
				}
				else {
					DragHandleAnchor handleAnchor;

					handleAnchor = _isResizing ? _resizeAnchor : HitTest(point);
					if (handleAnchor != DragHandleAnchor.None && _dragHandles[handleAnchor].
						  Enabled) {
						switch (handleAnchor) {
							case DragHandleAnchor.TopLeft:
							case DragHandleAnchor.BottomRight:
								cursor = Cursors.SizeNWSE;
								break;
							case DragHandleAnchor.TopCenter:
							case DragHandleAnchor.BottomCenter:
								cursor = Cursors.SizeNS;
								break;
							case DragHandleAnchor.TopRight:
							case DragHandleAnchor.BottomLeft:
								cursor = Cursors.SizeNESW;
								break;
							case DragHandleAnchor.MiddleLeft:
							case DragHandleAnchor.MiddleRight:
								cursor = Cursors.SizeWE;
								break;
							default:
								throw new ArgumentOutOfRangeException();
						}
					}
					else if (_isMoving || SelectionRegion.Contains(PointToImage(point))) {
						cursor = Cursors.SizeAll;
					}
					else {
						cursor = Cursors.Default;
					}
				}

				Cursor = cursor;
			}
		}

		private void CancelMove() {
			SelectionRegion = _previousSelectionRegion;
			CompleteMove();
		}

		private void CompleteMove() {
			ResetDrag();
			OnSelectionMoved(EventArgs.Empty);
		}

		private void CompleteResize() {
			ResetDrag();
			OnSelectionResized(EventArgs.Empty);
		}

		private DragHandleAnchor HitTest(Point cursorPosition) {
			return _dragHandles.HitTest(cursorPosition);
		}

		private bool IsOutsideDragZone(Point location) {
			Rectangle dragZone;
			int dragWidth;
			int dragHeight;

			dragWidth = SystemInformation.DragSize.Width;
			dragHeight = SystemInformation.DragSize.Height;
			dragZone = new Rectangle(_dragOrigin.X - (dragWidth / 2), _dragOrigin.Y - (dragHeight / 2), dragWidth, dragHeight);

			return !dragZone.Contains(location);
		}

		private void PositionDragHandles() {
			if (_dragHandles != null && _dragHandleSize > 0) {
				RectangleF selectionRegion;

				selectionRegion = SelectionRegion;

				if (selectionRegion.IsEmpty) {
					foreach (DragHandle handle in _dragHandles) {
						handle.Bounds = Rectangle.Empty;
					}
				}
				else {
					int left;
					int top;
					int right;
					int bottom;
					int halfWidth;
					int halfHeight;
					int halfDragHandleSize;
					Rectangle viewport;
					int offsetX;
					int offsetY;

					viewport = GetImageViewPort();
					offsetX = viewport.Left + Padding.Left + AutoScrollPosition.X;
					offsetY = viewport.Top + Padding.Top + AutoScrollPosition.Y;
					halfDragHandleSize = _dragHandleSize / 2;
					left = Convert.ToInt32((selectionRegion.Left * ZoomFactor) + offsetX);
					top = Convert.ToInt32((selectionRegion.Top * ZoomFactor) + offsetY);
					right = left + Convert.ToInt32(selectionRegion.Width * ZoomFactor);
					bottom = top + Convert.ToInt32(selectionRegion.Height * ZoomFactor);
					halfWidth = Convert.ToInt32(selectionRegion.Width * ZoomFactor) / 2;
					halfHeight = Convert.ToInt32(selectionRegion.Height * ZoomFactor) / 2;

					_dragHandles[DragHandleAnchor.TopLeft].
					  Bounds = new Rectangle(left - _dragHandleSize, top - _dragHandleSize, _dragHandleSize, _dragHandleSize);
					_dragHandles[DragHandleAnchor.TopCenter].
					  Bounds = new Rectangle(left + halfWidth - halfDragHandleSize, top - _dragHandleSize, _dragHandleSize, _dragHandleSize);
					_dragHandles[DragHandleAnchor.TopRight].
					  Bounds = new Rectangle(right, top - _dragHandleSize, _dragHandleSize, _dragHandleSize);
					_dragHandles[DragHandleAnchor.MiddleLeft].
					  Bounds = new Rectangle(left - _dragHandleSize, top + halfHeight - halfDragHandleSize, _dragHandleSize, _dragHandleSize);
					_dragHandles[DragHandleAnchor.MiddleRight].
					  Bounds = new Rectangle(right, top + halfHeight - halfDragHandleSize, _dragHandleSize, _dragHandleSize);
					_dragHandles[DragHandleAnchor.BottomLeft].
					  Bounds = new Rectangle(left - _dragHandleSize, bottom, _dragHandleSize, _dragHandleSize);
					_dragHandles[DragHandleAnchor.BottomCenter].
					  Bounds = new Rectangle(left + halfWidth - halfDragHandleSize, bottom, _dragHandleSize, _dragHandleSize);
					_dragHandles[DragHandleAnchor.BottomRight].
					  Bounds = new Rectangle(right, bottom, _dragHandleSize, _dragHandleSize);
				}
			}
		}

		private void ProcessSelectionMove(Point cursorPosition) {
			if (_isMoving) {
				int x;
				int y;
				Point imagePoint;
				Size viewSize;
				RectangleF selectionRegion;

				imagePoint = PointToImage(cursorPosition, false);
				viewSize = ViewSize;
				selectionRegion = SelectionRegion;

				x = Math.Max(0, imagePoint.X - _dragOriginOffset.X);
				if (x + selectionRegion.Width >= viewSize.Width) {
					x = viewSize.Width - (int)selectionRegion.Width;
				}

				y = Math.Max(0, imagePoint.Y - _dragOriginOffset.Y);
				if (y + selectionRegion.Height >= viewSize.Height) {
					y = viewSize.Height - (int)selectionRegion.Height;
				}

				SelectionRegion = new RectangleF(x, y, selectionRegion.Width, selectionRegion.Height);
			}
		}

		private void ProcessSelectionResize(Point cursorPosition) {
			if (_isResizing) {
				Point imagePosition;
				float left;
				float top;
				float right;
				float bottom;
				bool resizingTopEdge;
				bool resizingBottomEdge;
				bool resizingLeftEdge;
				bool resizingRightEdge;
				RectangleF selectionRegion;
				Size viewSize;

				imagePosition = PointToImage(cursorPosition);
				viewSize = ViewSize;

				// get the current selection
				selectionRegion = SelectionRegion;
				left = selectionRegion.Left;
				top = selectionRegion.Top;
				right = selectionRegion.Right;
				bottom = selectionRegion.Bottom;

				// decide which edges we're resizing
				resizingTopEdge = _resizeAnchor >= DragHandleAnchor.TopLeft && _resizeAnchor <= DragHandleAnchor.TopRight;
				resizingBottomEdge = _resizeAnchor >= DragHandleAnchor.BottomLeft && _resizeAnchor <= DragHandleAnchor.BottomRight;
				resizingLeftEdge = _resizeAnchor == DragHandleAnchor.TopLeft || _resizeAnchor == DragHandleAnchor.MiddleLeft || _resizeAnchor == DragHandleAnchor.BottomLeft;
				resizingRightEdge = _resizeAnchor == DragHandleAnchor.TopRight || _resizeAnchor == DragHandleAnchor.MiddleRight || _resizeAnchor == DragHandleAnchor.BottomRight;

				// and resize!
				if (resizingTopEdge) {
					top = imagePosition.Y > 0 ? imagePosition.Y : 0;

					if (bottom - top < MinimumSelectionSize.Height) {
						top = bottom - MinimumSelectionSize.Height;
					}
					else if (MaximumSelectionSize.Height > 0 && bottom - top > MaximumSelectionSize.Height) {
						top = bottom - MaximumSelectionSize.Height;
					}
				}
				else if (resizingBottomEdge) {
					bottom = imagePosition.Y < viewSize.Height ? imagePosition.Y : viewSize.Height;

					if (bottom - top < MinimumSelectionSize.Height) {
						bottom = top + MinimumSelectionSize.Height;
					}
					else if (MaximumSelectionSize.Height > 0 && bottom - top > MaximumSelectionSize.Height) {
						bottom = top + MaximumSelectionSize.Height;
					}
				}

				if (resizingLeftEdge) {
					left = imagePosition.X > 0 ? imagePosition.X : 0;

					if (right - left < MinimumSelectionSize.Width) {
						left = right - MinimumSelectionSize.Width;
					}
					else if (MaximumSelectionSize.Width > 0 && right - left > MaximumSelectionSize.Width) {
						left = right - MaximumSelectionSize.Width;
					}
				}
				else if (resizingRightEdge) {
					right = imagePosition.X < viewSize.Width ? imagePosition.X : viewSize.Width;

					if (right - left < MinimumSelectionSize.Width) {
						right = left + MinimumSelectionSize.Width;
					}
					else if (MaximumSelectionSize.Width > 0 && right - left > MaximumSelectionSize.Width) {
						right = left + MaximumSelectionSize.Width;
					}
				}

				SelectionRegion = new RectangleF(left, top, right - left, bottom - top);
			}
		}

		private void ResetDrag() {
			_isResizing = false;
			_isMoving = false;
			_dragOrigin = Point.Empty;
			_dragOriginOffset = Point.Empty;
		}

		private void StartResize(DragHandleAnchor anchor) {
			CancelEventArgs e;

			if (_isMoving || _isResizing) {
				throw new InvalidOperationException("A move or resize action is currently being performed.");
			}

			e = new CancelEventArgs();

			OnSelectionResizing(e);

			if (!e.Cancel) {
				_resizeAnchor = anchor;
				_previousSelectionRegion = SelectionRegion;
				_isResizing = true;
			}
		}

		#endregion
	}
}
